package com.planw.beetl.utils;

import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.PsiPlainTextImpl;
import com.intellij.psi.search.FilenameIndex;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.PsiTreeUtil;
import org.apache.commons.lang3.StringUtils;
import org.intellij.plugins.markdown.lang.MarkdownFileViewProvider;
import org.intellij.plugins.markdown.lang.MarkdownLanguage;
import org.intellij.plugins.markdown.lang.psi.impl.MarkdownHeaderImpl;
import org.jetbrains.annotations.NotNull;

import java.util.*;

/**
 * @author yanglin
 */
public class JavaService {

    private Project project;

    private JavaPsiFacade javaPsiFacade;

    public JavaService(Project project) {
        this.project = project;
        this.javaPsiFacade = JavaPsiFacade.getInstance(project);
    }

    public static JavaService getInstance(@NotNull Project project) {
        return ServiceManager.getService(project, JavaService.class);
    }


    public boolean checkIsBeetlSqlClass(PsiClass psiClass) {
        if (psiClass == null || !psiClass.isInterface()) {
            return false;
        }
        if (psiClass.getName().endsWith("Mapper") || psiClass.getName().endsWith("Dao")) {
            return true;
        }
        PsiClass[] interFaces = psiClass.getInterfaces();
        for (PsiClass interFace : interFaces) {
            if (interFace.getQualifiedName().equalsIgnoreCase("org.beetl.sql.core.mapper.BaseMapper")) {
                return true;
            }
        }
        return false;
    }

    public Map<String, List<PsiElement>> buildTargetPsiElement(List<PsiFile> psiFileList, Map<String, PsiMethod> psiMethodMap) {
        Map<String, List<PsiElement>> listMap = new HashMap<>();
        for (PsiFile psiFile : psiFileList) {
            List<PsiElement> psiElNeed = new LinkedList<>();
            Collection<MarkdownHeaderImpl> headers = null;
            if (psiFile.getLanguage() != MarkdownLanguage.INSTANCE) {
                MarkdownFileViewProvider viewProvider = new MarkdownFileViewProvider(psiFile.getManager(), psiFile.getVirtualFile(), true);
                PsiFile psiFile1 = viewProvider.getPsi(MarkdownLanguage.INSTANCE);
                headers = PsiTreeUtil.findChildrenOfType(psiFile1, MarkdownHeaderImpl.class);
            }else {
                headers = PsiTreeUtil.findChildrenOfType(psiFile, MarkdownHeaderImpl.class);
            }
            buildFilePsiElements(headers.toArray(new PsiElement[0]), listMap, psiMethodMap);
        }
        return listMap;
    }

    protected void buildFilePsiElements(PsiElement[] tempEls, Map<String, List<PsiElement>> listMap, Map<String, PsiMethod> psiMethodMap) {
        for (PsiElement tempEl : tempEls) {
            if(tempEl.getNode() == null || tempEl.getNode().getFirstChildNode() == null){
                continue;
            }
            String sqlId = StringUtils.trim(tempEl.getNode().getFirstChildNode().getText());
            if (psiMethodMap.containsKey(sqlId) || psiMethodMap.containsKey(StringUtils.removeEnd(sqlId, "$count"))) {
                List<PsiElement> elementList = listMap.get(sqlId);
                if (elementList == null) {
                    elementList = new LinkedList<>();
                    listMap.put(sqlId, elementList);
                }
                elementList.add(tempEl);
            }
        }
    }

    public List<PsiFile> getAllBeetlSqlFiles(Project project, PsiClass psiClass) {
        List<PsiFile> sqlFiles = new ArrayList<>();

        String sqlFileName = null;
        String path = null;
        String testAnFileName = findSqlResourceName(psiClass);
        if (StringUtils.isNotBlank(testAnFileName)) {
            path = StringUtils.replace(testAnFileName, ".", "/");
            String[] splitTest = StringUtils.split(testAnFileName, ".");
            sqlFileName = splitTest[splitTest.length - 1];
        }else {
            PsiClassType[] classTypes = psiClass.getExtendsListTypes();
            for (PsiClassType classType : classTypes) {
                String canonicalText = classType.getInternalCanonicalText();
                if (!StringUtils.startsWithIgnoreCase(canonicalText, "org.beetl.sql.") ||
                        !StringUtils.containsIgnoreCase(canonicalText, ".BaseMapper")) {
                    continue;
                }
                sqlFileName = StringUtils.capitalize(classType.getParameters()[0].getPresentableText());
            }
        }

        if (StringUtils.isBlank(sqlFileName)) {
            return sqlFiles;
        }
//        PsiMethod[] psiMethods = psiClass.getMethods();
//        if (psiMethods == null || psiMethods.length < 1) {
//            return sqlFiles;
//        }
//        Map<String, PsiMethod> psiMethodMap = new HashMap<>();
//        for (PsiMethod psiMethod : psiMethods) {
//            psiMethodMap.put(psiMethod.getName(), psiMethod);
//        }

        return getAllBeetlSqlFiles(project, sqlFileName, path);
    }


    public List<PsiFile> getAllBeetlSqlFiles(Project project, String className, String path) {
        List<PsiFile> psiFileList = new LinkedList<>();
        String sqlFileName = StringUtils.uncapitalize(className) + ".sql";
        loadPsiFiles(project, sqlFileName, psiFileList, path);
        sqlFileName = StringUtils.uncapitalize(className) + ".md";
        loadPsiFiles(project, sqlFileName, psiFileList, path);
        return psiFileList;
    }

    public void loadPsiFiles(Project project, String sqlFileName, List<PsiFile> psiFiles, String path) {
        PsiFile[] files = FilenameIndex.getFilesByName(project, sqlFileName, GlobalSearchScope.allScope(project));
        for (PsiFile file : files) {
            if (StringUtils.isNotBlank(path)) {
                if (!StringUtils.containsIgnoreCase(file.getVirtualFile().toString(), path)) {
                    continue;
                }
            }
            psiFiles.add(file);
        }
    }

    /**
     * 通过 SqlResource 注解的方式查找sql文件
     *
     * @param classType
     * @return
     */
    protected String findSqlResourceName(PsiClass classType) {
        PsiAnnotation[] annotations = classType.getAnnotations();
        if (annotations == null) {
            return null;
        }
        for (PsiAnnotation annotation : annotations) {
            String qualifiedName = annotation.getQualifiedName();
            if (StringUtils.startsWithIgnoreCase(qualifiedName, "org.beetl.sql.") &&
                    StringUtils.endsWithIgnoreCase(qualifiedName, ".SqlResource")) {
                PsiAnnotationParameterList list = annotation.getParameterList();
                for (PsiNameValuePair attribute : list.getAttributes()) {
                    return attribute.getLiteralValue();
                }
            }

        }
        return null;
    }

    public List<PsiClass> findMapperClass(String name) {
        return findMapperClass(name,project);
    }

    public List<PsiClass> findMapperClass(String name, Project project) {
        List<PsiClass> classList = new LinkedList<>();
        loadMapper(classList, name + "Dao", project);
        loadMapper(classList, name + "Mapper", project);
        return classList;
    }

    public Map<String, List<PsiMethod>> loadMapperPsiMethod(List<PsiClass> classMapperList) {
        Map<String, List<PsiMethod>> nameMethodsMap = new HashMap<>();
        for (PsiClass psiClass : classMapperList) {
            PsiMethod[] ms = psiClass.getMethods();
            for (PsiMethod m : ms) {
                List<PsiMethod> methods = nameMethodsMap.get(m.getName());
                if (methods == null) {
                    methods = new LinkedList<>();
                    nameMethodsMap.put(m.getName(), methods);
                }
                methods.add(m);
            }
        }
        return nameMethodsMap;
    }

    protected void loadMapper(List<PsiClass> classList, String name, Project project) {
        PsiClass[] psiClasses = JavaUtils.findClazzs(project, name);
        for (PsiClass psiClass : psiClasses) {
            if (psiClass.getSuperClass() == null) {
                continue;
            }
            PsiClass[] interFaces = psiClass.getInterfaces();
            for (PsiClass interFace : interFaces) {
                if (interFace.getQualifiedName().equalsIgnoreCase("org.beetl.sql.core.mapper.BaseMapper")) {
                    classList.add(psiClass);
                }
            }
        }
    }

}

